#include "ArcLru.hpp"
#include "ArcLfu.hpp"

namespace Kama
{
    template <typename Key, typename Value>
    class ArcCache : public KICachePolicy<Key, Value>
    {
    public:
        ArcCache(int capacity = 10, int transformThreshold = 3) : m_capacity(capacity), m_transformThreshold(transformThreshold)
        , lruPart(std::make_unique<ArcLru<Key, Value>>(capacity, transformThreshold))
        , lfuPart(std::make_unique<ArcLfu<Key, Value>>(capacity, transformThreshold))
        {}

        void put(const Key& key, const Value& value)
        {
            if (lruPart->checkGhost(key))
            {
                if (lfuPart->subtractCapacity())
                {
                    lruPart->addCapacity();
                }
                lruPart->put(key, value);
            }
            else if (lfuPart->checkGhost(key))
            {
                if (lruPart->subtractCapacity())
                {
                    lfuPart->addCapacity();
                }
                lfuPart->put(key, value);
            }
            else
            {
                if (lruPart->put(key, value))
                {
                    lfuPart->put(key, value);
                }
            }
        }

        bool get(const Key& key, Value& value)
        {
            if (lruPart->checkGhost(key))
            {
                if (lfuPart->subtractCapacity())
                {
                    lruPart->addCapacity();
                }
            }
            else if (lfuPart->checkGhost(key))
            {
                if (lruPart->subtractCapacity())
                {
                    lfuPart->addCapacity();
                }
            }
            bool shouldTransform = false;
            if (lruPart->get(key, value, shouldTransform))
            {
                if (shouldTransform)
                {
                    lfuPart->put(key, value);
                }
                return true;
            }
            return lfuPart->get(key, value);
        }

        Value get(const Key& key)
        {
            Value value;
            get(key, value);
            return value;
        }
    private:
        std::unique_ptr<ArcLru<Key, Value>> lruPart;
        std::unique_ptr<ArcLfu<Key, Value>> lfuPart;
        int m_capacity;
        int m_transformThreshold;
    };
}